Function.prototype.bind2 = function (context, ...args) {
    const fn = this
    args = args ? args : []
    return function newFn(...newFnArgs) {
        if (this instanceof newFn) {
            return new fn(...args, ...newFnArgs)
        }
        return fn.apply(context, [...args,...newFnArgs])
    }
}

Function.prototype.myBind = function (context, ...args) {
    context = context || window
    // 创造唯一的key值  作为我们构造的context内部方法名
    let key = Symbol();
    context[key] = this;
    let fn = this;
    const newFn = function (...newFnArgs) {
        // 第一种情况 :若是将 bind 绑定之后的函数当作构造函数，通过 new 操作符使用，则不绑定传入的 this，而是将 this 指向实例化出来的对象
        // 此时由于new操作符作用  this指向newFn实例对象  而newFn又继承自传入的_fn 根据原型链知识可得出以下结论
        // this.__proto__ === newFn.prototype   //this instanceof newFn =>true
        // this.__proto__.__proto__ === newFn.prototype.__proto__ === fn.prototype; //this instanceof fn =>true
        if (this instanceof newFn) {
            // 此时this指向指向newFn的实例  这时候不需要改变this指向
            this[key] = fn;
            this[key](...[...args, ...newFnArgs]); //这里使用es6的方法让bind支持参数合并
            delete this[fn];
        } else {
            // 如果只是作为普通函数调用  那就很简单了 直接改变this指向为传入的context
            context[key](...[...args, ...newFnArgs]);
            delete context[key];
        }
    };
    // 如果绑定的是构造函数 那么需要继承构造函数原型属性和方法 一些情况下函数没有prototype，比如箭头函数
    // 实现继承的方式: 使用Object.create
    if (fn.prototype) {
        newFn.prototype = Object.create(this.prototype);
    }
    return newFn;
};

const obj1 = {
    name: 'joy',
    getName() {
        console.log(this.name); 
    }
};

const obj2 = {
    name: 'bind'
};

obj1.getName.call(obj2); //obj2 bind
obj1.getName.apply(obj2); //obj2 bind
const fn = obj1.getName.bind(obj2);
fn();//obj2 bind
const fn2 = obj1.getName.bind2(obj2);
fn2();//obj2 bind